package msgbus

import (
	"encoding/json"
	"fmt"
	"gitee.com/sansaniot/ssiot-gw-lib/common/global"
	"sync/atomic"
	"time"

	"github.com/goinggo/mapstructure"

	"github.com/rs/zerolog/log"
)

var (
	LOGIN          = "login"
	ACK_LOGIN      = "login/ack"
	LOGOUT         = "logout"
	SENSOR_POST    = "sensor"
	SENSOR_GET     = "sensor/get"
	ACK_SENSOR_GET = "sensor/get/ack"
	SENSOR_SET     = "sensor/set"
	ACK_SENSOR_SET = "sensor/set/ack"
	PROPS_GET      = "props/get"
	ACK_PROPS_GET  = "props/get/ack"
	PROPS_SET      = "props/set"
	ACK_PROPS_SET  = "props/set/ack"
	PROPS_POST     = "props"
	CONFIG_GET     = "config/get"
	ACK_CONFIG_GET = "config/get/ack"
	CONFIG_SET     = "config/set"
	ACK_CONFIG_SET = "config/set/ack"
)

type MsgIoT struct {
	Biz  string      `json:"-"`
	Sid  uint64      `json:"sid,omitempty"`
	To   string      `json:"to,omitempty"`
	From string      `json:"from,omitempty"`
	Code int         `json:"code,omitempty"`
	Msg  string      `json:"msg,omitempty"`
	Data interface{} `json:"data,omitempty"`
}

type MsgLogin struct {
	Model string `json:"model"`
}

type MsgAckLogin struct {
	Ak   string `json:"ak"`
	Goto string `json:"goto"`
}

type MsgConfigGet struct {
	Path string `json:"path"`
}

type MsgAckConfigGet struct {
}

type MsgConfigSet struct {
}

type MsgAckConfigSet struct {
}

func NewMsgIoT(msg string, to string, from string, data interface{}) *MsgIoT {
	iot := new(MsgIoT)
	iot.Biz = msg
	iot.To = to
	iot.From = from
	iot.Data = data
	return iot
}

func (this *MsgIoT) GetLogin() *MsgLogin {
	msg := &MsgLogin{}
	if err := mapstructure.Decode(this.Data, msg); err != nil {
		log.Err(err).Msgf("decode login msg error")
		return nil
	}
	return msg
}

func (this *MsgIoT) GetAckLogin() *MsgAckLogin {
	msg := &MsgAckLogin{}
	if err := mapstructure.Decode(this.Data, msg); err != nil {
		log.Err(err).Msgf("decode login msg error")
		return nil
	}
	return msg
}

func (this *MsgIoT) Publish() bool {
	if this.To == "" {
		this.To = "$"
	}

	topic = fmt.Sprintf("/iot/$/%s/%s", global.Sn, this.Biz)

	if data, err := json.Marshal(this); err != nil {
		log.Err(err).Msgf("marshal error. %s", this.Biz)
		return false
	} else {
		if token := mqttcli.Publish(topic, 0, false, data); token.Wait() && token.Error() != nil {
			log.Err(token.Error()).Msgf("publish error. %s", this.Biz)
			return false
		}
		log.Trace().Msgf("%s", topic)
	}

	return true
}

func (this *MsgIoT) SyncPublish() *MsgIoT {
	if this.To == "" {
		this.To = "$"
	}

	if this.Sid == 0 {
		this.Sid = atomic.AddUint64(&sid, 1)
	}

	from := ""
	if this.From != "" {
		from = this.From
	} else {
		from = global.Sn
	}

	topic = fmt.Sprintf("/iot/$/%s/%s", from, this.Biz)

	iotToken = NewSSIoTToken()
	iotToken.req = this

	if data, err := json.Marshal(this); err != nil {
		log.Error().Msgf("%s\n", err)
		return nil
	} else {
		if token := mqttcli.Publish(topic, 0, false, data); token.Wait() && token.Error() != nil {
			return nil
		}
		log.Trace().Msgf("%s", topic)

		if !iotToken.WaitTimeout(time.Second * 5) {
			return nil
		}
	}
	return iotToken.ack
}
